/**
 *
 */
package de.cbf.cam.dsa.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import de.cbf.cam.dsa.model.base.BaseWeapon;
import de.cbf.cam.dsa.model.base.TalentTmpl;
import de.cbf.cam.dsa.model.entities.ArmorPart;
import de.cbf.cam.dsa.model.entities.BattleChar;
import de.cbf.cam.dsa.model.entities.OneAction;
import de.cbf.cam.dsa.model.entities.OneAttribute;
import de.cbf.cam.dsa.model.entities.OneEnergy;
import de.cbf.cam.dsa.model.entities.OneNPC;
import de.cbf.cam.dsa.model.entities.OneTalent;
import de.cbf.cam.dsa.model.entities.OneWound;
import de.cbf.cam.dsa.model.entities.SF;
import de.cbf.cam.dsa.model.entities.WeaponTalent;
import de.cbf.cam.dsa.model.entities.WoundParameter;
import de.cbf.cam.dsa.model.entities.constants.TalentConstants;
import de.cbf.cam.dsa.model.entities.constants.WeaponStyleTemplates;
import de.cbf.cam.dsa.model.entities.constants.WearPlaces;
import de.cbf.cam.dsa.model.entities.constants.Zones;
import de.cbf.cam.dsa.model.entities.items.Item;
import de.cbf.cam.dsa.model.entities.maneuvers.ManeuverDesc;
import de.cbf.cam.dsa.model.entities.maneuvers.OneManeuver;
import de.cbf.cam.dsa.model.entities.states.OneState;
import de.cbf.cam.dsa.model.entities.states.StateBloodLoss;
import de.cbf.cam.dsa.model.entities.states.StateLimbUnusable;
import de.cbf.cam.dsa.model.entities.states.StateNoCloseCombat;
import de.cbf.cam.dsa.model.entities.states.StateOnGround;
import de.cbf.cam.dsa.model.entities.states.StateUnconscious;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerATPA;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerAttribute;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerEnergy;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerINI;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerState;
import de.cbf.cam.dsa.model.entities.weapon.OneWeapon;
import de.cbf.cam.dsa.model.entities.weapon.WeaponInUse;
import de.cbf.cam.dsa.model.entities.weapon.WeaponMelee;
import de.cbf.cam.dsa.model.persistence.Persister;

/**
 * @author Nebelritter
 *
 */
public class ModelBase {

	protected static Logger logger = Logger.getLogger(ModelBase.class);

	private static ModelBase instance = new ModelBase();

	//base data structures
	/**
	 * @uml.property  name="sfCache"
	 * @uml.associationEnd  qualifier="name:java.lang.String de.cbf.cam.dsa.model.entities.SF"
	 */
	private HashMap<String,SF> sfCache = new HashMap<String,SF>();

	/**
	 * @uml.property  name="talentCache"
	 * @uml.associationEnd  multiplicity="(0 -1)" ordering="true" elementType="java.lang.String" qualifier="talentName:java.lang.String de.cbf.cam.dsa.model.base.TalentTmpl"
	 */
	private Map<String, TalentTmpl>			talentCache 		= new HashMap<String, TalentTmpl>	(43);
	/**
	 * @uml.property  name="weaponsCache"
	 * @uml.associationEnd  qualifier="type:java.lang.String de.cbf.cam.dsa.model.entities.weapon.OneWeapon"
	 */
	private Map<String, BaseWeapon> 		weaponsCache 		= new HashMap<String, BaseWeapon>	(43);
	/**
	 * @uml.property  name="itemsCache"
	 * @uml.associationEnd  qualifier="name:java.lang.String de.cbf.cam.dsa.model.entities.items.Item"
	 */
	private Map<String, Item> 				itemsCache 			= new HashMap<String, Item>			(43);
	/**
	 * @uml.property  name="armorCache"
	 * @uml.associationEnd  qualifier="name:java.lang.String de.cbf.cam.dsa.model.entities.ArmorPart"
	 */
	private Map<String, ArmorPart> 			armorCache 			= new HashMap<String, ArmorPart>	(43);
	/**
	 * @uml.property  name="maneuverDescCache"
	 * @uml.associationEnd  qualifier="getManeuverName:java.lang.String de.cbf.cam.dsa.model.entities.maneuvers.ManeuverDesc"
	 */
	private HashMap<String,ManeuverDesc> 	maneuverDescCache 	= new HashMap<String,ManeuverDesc>	(43);
	/**
	 * @uml.property  name="maneuverCache"
	 * @uml.associationEnd  qualifier="name:java.lang.String de.cbf.cam.dsa.model.entities.maneuvers.OneManeuver"
	 */
	private HashMap<String,OneManeuver> 	maneuverCache 		= new HashMap<String,OneManeuver>	(43);

	public static ModelBase getInstance(){
		return instance;
	}

	public OneAction getActionForCharacter(int type,BattleChar actor){
		//find possible maneuvers for action
		List<ManeuverDesc> possibleManeuvers = new ArrayList<ManeuverDesc>();
		//
		Collection<ManeuverDesc> maneuvers = maneuverDescCache.values();
		for (Iterator<ManeuverDesc> iterator = maneuvers.iterator(); iterator.hasNext();) {
			ManeuverDesc desc = (ManeuverDesc) iterator.next();
			if(desc.isPossibleInAction(type)){
				possibleManeuvers.add(desc);
			}
		}
		//build action
		OneAction action = new OneAction(actor);
		action.setType(type);
		action.setPossibleManeuvers(possibleManeuvers);
		return action;
	}

	public WeaponMelee provideUnarmed(){

		WeaponMelee weapon = new WeaponMelee();
		weapon.setWeaponType(OneWeapon.UNARMED);
		weapon.setBf(-12);
		weapon.setDkBits(WeaponMelee.DKH);
		weapon.setIniMod(0);
		weapon.setWeapLength(0);
		weapon.setPrice(-1);
		weapon.setTpBonus(0);
		weapon.setTpDiceNumber(1);
		weapon.setTpDiceSides(6);
		weapon.setKKTreshold(10);
		weapon.setKKStep(3);
		weapon.setWeight(0);
		weapon.setWmAT(0);
		weapon.setWmPA(0);
		weapon.setWear(WearPlaces.Hand);
		weapon.addStyle(WeaponStyleTemplates.provideUnarmedRingen());
		weapon.addStyle(WeaponStyleTemplates.provideUnarmedRaufen());
		weapon.addStyle(WeaponStyleTemplates.provideUnarmedHruruzat());

		return weapon;
	}

	public BaseWeapon getVirginWeapon(String type){
		BaseWeapon weapon = weaponsCache.get(type);
		if(weapon == null){
			//find weapon in DB
		}
		return weapon;
	}


	public void addVirginWeapon(OneWeapon weapon, boolean persist) {
		if(!persist){
			weaponsCache.put(weapon.getWeaponType(),weapon);
		}else{
			if(!isAlreadyKnownWeapon(weapon)){
				//add weapon to DB
			}
		}
	}

	private boolean isAlreadyKnownWeapon(OneWeapon weapon) {
		BaseWeapon known = Persister.getInstance().getBaseWeapon(weapon.getWeaponType());
		if(known != null){
			return false;
		}else{
			return true;
		}
	}

	public void setWeapons(List<OneWeapon> weapons,boolean persist){
		for(int i=0;i<weapons.size();i++){
			addVirginWeapon(weapons.get(i), persist);
		}
	}
//========================
//	SFs
//========================
	public SF getSF(String name){
		SF result = sfCache.get(name);
		if(result!=null){
			return (SF)result.clone();
		}
		logger.error("Sf not found: "+name);
		return null;
	}

	public void registerSF(SF sf,boolean persist){
		SF prevSF=sfCache.put(sf.getName(),sf);
		if(prevSF!=null){
			//collision
			logger.error(
					"Error in SFConstants: sf not registered: "+sf+
					" other sf in place:"+prevSF);
		}
	}

	public void addSFs(List<SF> sfs,boolean persist) {
		for(int i=0;i<sfs.size();i++){
			SF sf = sfs.get(i);
			registerSF(sf, persist);
		}
	}

//	============================
//		Talents
//	============================
	public void registerTalentTemplate(TalentTmpl talentTmpl,boolean persist){
		int preSize =talentCache.size();
		talentCache.put(talentTmpl.getName(), talentTmpl);
		if(talentCache.size()<=preSize){
			logger.error("TalentTmpl"+talentTmpl.getName()+" not registered!");
		}
	}

	public void setTalentTemplates(List<TalentTmpl> talentTmpls,boolean persist){
		for(int i=0;i<talentTmpls.size();i++){
			TalentTmpl talent = talentTmpls.get(i);
			registerTalentTemplate(talent,persist);
		}
		if(talentTmpls.size()!=this.talentCache.size()){
			logger.error("Error in Talentregistrar: Not all Talents registered: "+this.talentCache.size()+"/"+talentTmpls.size());
		}
	}

	public OneTalent getTalentForCharacter(OneNPC oneNPC,String name,int value){
		OneTalent resultTalent=null;
		//get template
		TalentTmpl template = talentCache.get(name);
		if(template!=null){
			//check for talentType
			switch (template.getTalentType()) {
				case TalentTmpl.TYPE_WeaponTalent:{
					resultTalent = new WeaponTalent(template, oneNPC, value);
					break;
				}
				case TalentTmpl.TYPE_Talent_Body:{
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
				case TalentTmpl.TYPE_Talent_Society:{
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
				case TalentTmpl.TYPE_Talent_Nature:{
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
				case TalentTmpl.TYPE_Talent_Knowledge:{
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
				case TalentTmpl.TYPE_Talent_Handcraft:{
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
				default:{
					logger.error("Talent with name: "+name+" no acceptable talentType -> returning simple talent");
					resultTalent = new OneTalent(template, oneNPC, value);
					break;
				}
			}
		}else{
			logger.error("Talent with name: "+name+" not found ");
		}
		return resultTalent;
	}

	public List<OneTalent> addBaseTalentsBody(OneNPC character,
			int klettern,int koerper,int schleichen,int schwimmen,int selbst,int verstecken,int singen,int sinnen,
			int tanzen,int zechen){
		List<OneTalent> resultList = new ArrayList<OneTalent>();

		resultList.add(getTalentForCharacter(character, TalentConstants.Klettern,klettern));
		resultList.add(getTalentForCharacter(character, TalentConstants.Koerperbeherrschung,koerper));
		resultList.add(getTalentForCharacter(character, TalentConstants.Schleichen,schleichen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Schwimmen,schwimmen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Selbstbeherrschung,selbst));
		resultList.add(getTalentForCharacter(character, TalentConstants.SichVerstecken,verstecken));
		resultList.add(getTalentForCharacter(character, TalentConstants.Singen,singen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Sinnenschaerfe,sinnen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Tanzen,tanzen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Zechen,zechen));


		return resultList;
	}

	public List<OneTalent> addBaseTalentsSociety(OneNPC character,
			int menschenk,int ueberreden){
		List<OneTalent> resultList = new ArrayList<OneTalent>();

		resultList.add(getTalentForCharacter(character, TalentConstants.Menschenkenntnis,menschenk));
		resultList.add(getTalentForCharacter(character, TalentConstants.Ueberreden,ueberreden));

		return resultList;
	}

	public List<OneTalent> addBaseTalentsNature(OneNPC character,
			int faehrten,int orientierung,int wildnis){
		List<OneTalent> resultList = new ArrayList<OneTalent>();

		resultList.add(getTalentForCharacter(character, TalentConstants.Faehrtensuche,faehrten));
		resultList.add(getTalentForCharacter(character, TalentConstants.Orientierung,orientierung));
		resultList.add(getTalentForCharacter(character, TalentConstants.Wildnisleben,wildnis));

		return resultList;
	}

	public List<OneTalent> addBaseTalentsKnowledge(OneNPC character,
			int goetter,int rechnen,int sagen){
		List<OneTalent> resultList = new ArrayList<OneTalent>();

		resultList.add(getTalentForCharacter(character, TalentConstants.Goetter,goetter));
		resultList.add(getTalentForCharacter(character, TalentConstants.Rechnen,rechnen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Sagen,sagen));

		return resultList;
	}

	public List<OneTalent> addBaseTalentsHandcraft(OneNPC character,
			int wunden,int holz,int kochen,int leder,int malen,int schneidern){

		List<OneTalent> resultList = new ArrayList<OneTalent>();

		resultList.add(getTalentForCharacter(character, TalentConstants.HKWunden,wunden));
		resultList.add(getTalentForCharacter(character, TalentConstants.Holzbearbeitung,holz));
		resultList.add(getTalentForCharacter(character, TalentConstants.Kochen,kochen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Lederarbeiten,leder));
		resultList.add(getTalentForCharacter(character, TalentConstants.Malen,malen));
		resultList.add(getTalentForCharacter(character, TalentConstants.Schneidern,schneidern));
		return resultList;
	}
	/**
	 * method to find a compensation talent for a given talent and character<br>
	 * Does not reduce talent value by compensation factor<br>
	 * use OneTalent.getCompensateValueForTalent(compensationTalent.getName())<br>
	 * for compensation
	 * @param character
	 * @param talentName
	 * @return
	 */
	public OneTalent findBestCompensationTalentAviable(OneNPC character,String talentName) {
		OneTalent resultTalent=null;
		//if character or talent name unaviable -> return null -> no compensation possible
		if(character==null||talentName==null) return resultTalent;
		//get the talent (without character) to evaluate compensation talentCache
		TalentTmpl talentTemp = talentCache.get(talentName);
		//if talent exists in registry -> else return null
		if(talentTemp!=null){
			//get list of compensation talent names
			List<String> compensateTalents=talentTemp.getCompensateTalentsList();
			int resultValue=0;
			for(int j =0;j<compensateTalents.size();j++){
				//look at each compensation talent
				OneTalent compensateTalent = character.getTalent(compensateTalents.get(j));
				//if character has got talent
				if(compensateTalent!=null){
					//get compensation value
					int compensateValue=talentTemp.getCompensateValueForTalent(compensateTalent.getName());
					if(resultTalent==null){
						//first talent available is used to compare
						resultTalent = compensateTalent;
						//calculate reduced value for choice decision
						resultValue  = resultTalent.getTalValue()+compensateValue;
					}else{
						//compare reduced values and choose if reduced value of new is higher
						if(compensateTalent.getTalValue()+compensateValue>resultValue){
							resultTalent = compensateTalent;
							//calculate reduced value for choice decision
							resultValue  = compensateTalent.getTalValue()+compensateValue;
						}
					}
				}
			}
			//do not reduce by compensation value!! At battle reduction is handled separately
		}
		return resultTalent;
	}
	public List<TalentTmpl> getAllTalentTemplates() {
		List<TalentTmpl> result = new ArrayList<TalentTmpl>();
		result.addAll(this.talentCache.values());
		return result;
	}

	public Item getItemByName(String name) {
		Item item = itemsCache.get(name);
		if(item == null){
			//return fitting item /Weapon/armor part etc from DB
			item = Persister.getInstance().getItemByName(name);
		}
		return item;
	}

	public void addItem(Item item,boolean persist) {
		if(!persist){
			itemsCache.put(item.getName(), item);
		}else{
			//add to DB
		}
	}

	public Item getArmorByName(String name) {
		ArmorPart armorPart = armorCache.get(name);
		if(armorPart == null){
			//return fitting item /Weapon/armor part etc from DB
			armorPart = Persister.getInstance().getArmorByName(name);
		}
		return armorPart;
	}

	public void addArmorPart(ArmorPart armorPart,boolean persist) {
		if(!persist){
			armorCache.put(armorPart.getName(), armorPart);
		}else{
			//add to DB
		}

	}



//========================
//			maneuver
//========================
	public OneManeuver getManeuver(String name){
		OneManeuver result = maneuverCache.get(name);
		if(result == null){
			logger.error("Error:Maneuver not found:"+name);
			return null;
		}
		return (OneManeuver)result.clone();
	}

	public void registerManeuver(OneManeuver maneuver,boolean persist){
		if(!persist){
			OneManeuver prevManeuver=maneuverCache.put(maneuver.getManeuverName(),maneuver);
			if(prevManeuver!=null){
				//collision
				logger.error(
						"Error in ModelBase: maneuver not registered: "+maneuver+
						"other maneuver in place:"+prevManeuver);
			}
		}else{
			// add to db
		}

	}
	public List<ManeuverDesc> getAllManeuverDescs() {
		List<ManeuverDesc> result = new ArrayList<ManeuverDesc>();
		result.addAll(maneuverDescCache.values());
		return result;
	}

	public void setManeuverDescs(List<ManeuverDesc> maneuverDescs,boolean persist) {
		for(int i=0;i<maneuverDescs.size();i++){
			ManeuverDesc maneuver = maneuverDescs.get(i);
			registerManeuverDesc(maneuver, persist);
		}
	}
	public void registerManeuverDesc(ManeuverDesc maneuverDesc,boolean persist){
		if(!persist){
			ManeuverDesc prevManeuver = maneuverDescCache.put(maneuverDesc.getManeuverName(),maneuverDesc);
			if(prevManeuver!=null){
				//collision
				logger.error(
						"Error in ModelBase: maneuverDesc not registered: "+maneuverDesc+
						"other maneuver in place:"+prevManeuver);
			}
		}else{
			// add to db
		}

	}
	public List<OneManeuver> getAllManeuvers() {
		List<OneManeuver> result = new ArrayList<OneManeuver>();
		result.addAll(maneuverCache.values());
		return result;
	}

	public void setManeuvers(List<OneManeuver> maneuvers,boolean persist) {
		for(int i=0;i<maneuvers.size();i++){
			OneManeuver maneuver = maneuvers.get(i);
			registerManeuver(maneuver, persist);
		}
	}
	public List<OneManeuver> getManeuversForDescriptions(List<ManeuverDesc> descriptions){
 		List<OneManeuver> resultList = new ArrayList<OneManeuver>();
 		for(int i = 0;i<descriptions.size();i++){
 			ManeuverDesc description = descriptions.get(i);
 			OneManeuver maneuver = getManeuver(description.getManeuverName());
 			maneuver.setModifier(description.getModifier());
 			resultList.add(maneuver);
 		}
 		return resultList;
	}

	/**
	 * get maneuver possible for this character by his abilitys (sf,talents)
	 * @param character
	 * @return
	 */
	public List<ManeuverDesc> getPossibleManeuversForCharacter(OneNPC character){
		List<ManeuverDesc> resultList = new ArrayList<ManeuverDesc>();
		List<ManeuverDesc> descriptions = new ArrayList<ManeuverDesc>();
		descriptions.addAll(maneuverDescCache.values());
		for(int i = 0;i<descriptions.size();i++){
			ManeuverDesc maneuver = descriptions.get(i);
			if(maneuver.isPossibleForCharacter(character)){
				resultList.add(maneuver);
			}
		}
		return resultList;
	}

	/**
	 * filter possible maneuvers by abilitys by maneuver possible because of weapons
	 * @param character
	 * @return
	 */
	public List<ManeuverDesc> getPossibleManeuversArmedCharacter(OneNPC character){
		List<ManeuverDesc> resultList = new ArrayList<ManeuverDesc>();
		//filter by main weapon and second weapon
		WeaponInUse mainWeapon = character.getWeapon(OneNPC.Hand_Main);
		List<ManeuverDesc> mainWeaponManeuvers = mainWeapon.getActiveStyle().getPossibleManeuvers();
		WeaponInUse secondWeapon = character.getWeapon(OneNPC.Hand_Second);
		List<ManeuverDesc> secondWeaponManeuvers = secondWeapon.getActiveStyle().getPossibleManeuvers();

		for(int i = 0;i<mainWeaponManeuvers.size();i++){
			ManeuverDesc maneuver = mainWeaponManeuvers.get(i);
			if(maneuver.isPossibleForCharacter(character)){
				resultList.add(maneuver);
			}
		}
		for(int i = 0;i<secondWeaponManeuvers.size();i++){
			ManeuverDesc maneuver = secondWeaponManeuvers.get(i);
			if(maneuver.isPossibleForCharacter(character)){
				if(!resultList.contains(maneuver)){
					resultList.add(maneuver); //no double adding
				}
			}
		}
		return resultList;
	}
	/**
	 * filter by the actions the character has left
	 * @param character
	 * @return
	 */
	public List<ManeuverDesc> getPossibleManeuversForCharacter(BattleChar character){

		List<ManeuverDesc> maneuvers = new ArrayList<ManeuverDesc>();
		maneuvers.addAll(getPossibleManeuversArmedCharacter(character.getCharacter()));

		//check if there is an action available
		List<OneAction> actions = character.getActions(false);	//get unrealized actions
		if(actions.size()<=0)return  new ArrayList<ManeuverDesc>(); //no action left unrealized -> no maneuver possible

		List<ManeuverDesc> possibleManeuversByActions = new ArrayList<ManeuverDesc>();
		//initial data, we have at least one action or would have returned before

		//check for which are impossible by actions, index 1 cause first action already used
		for(int i=0;i<actions.size();i++){
			OneAction action = actions.get(i);
			for(int j = 0;j<action.getPossibleManeuvers().size();j++){
				ManeuverDesc maneuver = action.getPossibleManeuvers().get(j);
				if(!possibleManeuversByActions.contains(maneuver)){
					possibleManeuversByActions.add(maneuver);
				}
			}
		}
		maneuvers.retainAll(possibleManeuversByActions);
		return maneuvers;
	}



	public List<OneManeuver> filterByAlreadyChosen(List<OneManeuver> candidates,List<OneManeuver> alreadyChosen){
		List<OneManeuver> resultList = new ArrayList<OneManeuver>();
		for(int i = 0;i<candidates.size();i++){
			OneManeuver chosenManeuver = candidates.get(i);
			if(chosenManeuver.isPossibleFromAlreadyChosenManeuvers(alreadyChosen)){;
				resultList.add(chosenManeuver);
			}
		}
		return resultList;
	}

	public List<OneManeuver> filterByAttackerManeuvers(
			List<OneManeuver> maneuvers, List<OneManeuver> attackerManeuvers) {
		//remove by attacker excluded maneuvers
		for(int i = 0;i<attackerManeuvers.size();i++){
			OneManeuver attackerManeuver = attackerManeuvers.get(i);
			List<String> excludedManeuvers = attackerManeuver.getExclDefMan();
			for(int j = 0;j<excludedManeuvers.size();j++){
				String excludedManeuver = excludedManeuvers.get(j);
				for(int k = 0;k<maneuvers.size();k++){
					OneManeuver maneuver = maneuvers.get(k);
					if(maneuver.getManeuverName().equals(excludedManeuver)){
						maneuvers.remove(maneuver);
					}
				}
			}
		}
		return maneuvers;
	}

	/**
	 * filter if attack/defense maneuvers are wanted
	 * @param originalList
	 * @param maneuverType
	 * @return
	 */
	public List<OneManeuver> filterByType(List<OneManeuver> originalList,int maneuverType){
		List<OneManeuver> resultList = new ArrayList<OneManeuver>();
		for (Iterator<OneManeuver> iterator = originalList.iterator(); iterator.hasNext();) {
			OneManeuver maneuver = (OneManeuver) iterator.next();
			if((maneuver.getManeuverType()&maneuverType)==maneuverType){
				resultList.add(maneuver);
			}
		}
		return resultList;
	}

	public List<OneManeuver> getManeuversForBattleCharacterByType
	(BattleChar character,List<OneManeuver> alreadyChosen,int maneuverType){

		List<ManeuverDesc> maneuverDescriptions = getPossibleManeuversForCharacter(character);

		List<OneManeuver> maneuvers = getManeuversForDescriptions(maneuverDescriptions);

		maneuvers = filterByAlreadyChosen(maneuvers, alreadyChosen);

		maneuvers = filterByType(maneuvers, maneuverType);

		return maneuvers;
	}

	public List<OneManeuver> getManeuversForBattleCharacterByType(
			BattleChar character, List<OneManeuver> alreadyChosen,
			List<OneManeuver> attackerManeuvers, int maneuverType) {

		List<OneManeuver> maneuvers = getManeuversForBattleCharacterByType(
				character, alreadyChosen, maneuverType);

		maneuvers = filterByAttackerManeuvers(maneuvers, attackerManeuvers);

		return maneuvers;
	}

//	========================
//		Wounds
//	========================

	public List<WoundParameter> getWoundParameters(String zone, int prevWoundCount ){
		List<WoundParameter> parameters = new ArrayList<WoundParameter>();

		if(prevWoundCount<3){
			//nonfinal wounds
			if(zone.equals(Zones.Head)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "INI-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.Breast)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.MainArm)){
				return parameters;
			}
			if(zone.equals(Zones.SecondArm)){
				return parameters;
			}
			if(zone.equals(Zones.Stomach)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.MainLeg)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.SecondLeg)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.Back)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = false;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);
				return parameters;
			}
		}else{
			//final wounds
			if(zone.equals(Zones.Head)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = true;
				parameter.description = "LE-Verlust";
				parameter.minValue = 1;
				parameter.maxValue = 6;
				parameters.add(parameter);

				parameter = new WoundParameter();
				parameter.finalWound = true;
				parameter.zone 	= zone;
				parameter.description = "Bewusstlos";
				parameter.minValue = 1;
				parameter.maxValue = 20;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.Breast)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = true;
				parameter.description = "Bewusstlos";
				parameter.minValue = 1;
				parameter.maxValue = 20;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.MainArm)){
				return parameters;
			}
			if(zone.equals(Zones.SecondArm)){
				return parameters;
			}
			if(zone.equals(Zones.Stomach)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = true;
				parameter.description = "Bewusstlos";
				parameter.minValue = 1;
				parameter.maxValue = 20;
				parameters.add(parameter);
				return parameters;
			}
			if(zone.equals(Zones.MainLeg)){
				return parameters;
			}
			if(zone.equals(Zones.SecondLeg)){
				return parameters;
			}
			if(zone.equals(Zones.Back)){
				WoundParameter parameter = new WoundParameter();
				parameter.zone 	= zone;
				parameter.finalWound = true;
				parameter.description = "Bewusstlos";
				parameter.minValue = 1;
				parameter.maxValue = 20;
				parameters.add(parameter);
				return parameters;
			}
		}
		return parameters;
	}

	public OneWound getWound(WoundParameter parameter,List<Integer> parameters){
		OneWound wound = null;
		try{
			if(!parameter.finalWound){
				//non final wounds
				String zone = parameter.zone;
				if(zone.equals(Zones.Head)){
					wound = provideHeadWound(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.Breast)){
					wound = provideBreastWound(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.MainArm)){
					wound = provideMainArmWound();
					return wound;
				}
				if(zone.equals(Zones.SecondArm)){
					wound = provideSecondArmWound();
					return wound;
				}
				if(zone.equals(Zones.Stomach)){
					wound = provideStomachWound(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.MainLeg)){
					wound = provideMainLegWound(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.SecondLeg)){
					wound = provideSecondLegWound(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.Back)){
					wound = provideBackWound(parameters.get(0));
					return wound;
				}
			}else{
				//final wounds
				String zone = parameter.zone;
				if(zone.equals(Zones.Head)){
					wound = provideHeadWoundFinal(parameters.get(0), parameters.get(1));
					return wound;
				}
				if(zone.equals(Zones.Breast)){
					wound = provideBreastWoundFinal(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.MainArm)){
					wound = provideMainArmWoundFinal();
					return wound;
				}
				if(zone.equals(Zones.SecondArm)){
					wound = provideSecondArmWoundFinal();
					return wound;
				}
				if(zone.equals(Zones.Stomach)){
					wound = provideStomachWoundFinal(parameters.get(0));
					return wound;
				}
				if(zone.equals(Zones.MainLeg)){
					wound = provideMainLegWoundFinal();
					return wound;
				}
				if(zone.equals(Zones.SecondLeg)){
					wound = provideSecondLegWoundFinal();
					return wound;
				}
				if(zone.equals(Zones.Back)){
					wound = provideBackWoundFinal(parameters.get(0));
					return wound;
				}
			}
		}catch (ArrayIndexOutOfBoundsException e) {
			Activator.logger.error("too few woundParameters: "+parameter+" Exception: "+ e);
		}

		if(wound == null){
			Activator.logger.error("Wound for zone: "+parameter+" not found in ModelBase");
		}
		return null;
	}

	private OneWound provideHeadWound(int  iniLoss) {
		OneWound wound = new OneWound(Zones.Head);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.MU,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KL,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.IN,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.INIBase,-2,false));

		wound.addStateChangerWound(new StateChangerINI(iniLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideHeadWoundFinal(int leLoss,int roundsUnconscious) {
		OneWound wound = new OneWound(Zones.Head);
		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateUnconscious(roundsUnconscious));
		states.add(new StateBloodLoss(-1));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}

	private OneWound provideBreastWound(int leLoss) {
		OneWound wound = new OneWound(Zones.Breast);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KO,-1,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KK,-1,false));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1,OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1,OneNPC.Hand_Second));
		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideBreastWoundFinal(int roundsUnconscious) {
		OneWound wound = new OneWound(Zones.Breast);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateUnconscious(roundsUnconscious));
		states.add(new StateBloodLoss(-1));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}

	private OneWound provideMainArmWound() {
		OneWound wound = new OneWound(Zones.MainArm);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.FF,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KK,-2,false));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Main));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideMainArmWoundFinal() {
		OneWound wound = new OneWound(Zones.MainArm);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateLimbUnusable(OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}
	private OneWound provideSecondArmWound() {
		OneWound wound = new OneWound(Zones.SecondArm);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.FF,-2,true));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KK,-2,true));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Second));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideSecondArmWoundFinal() {
		OneWound wound = new OneWound(Zones.SecondArm);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateLimbUnusable(OneNPC.Hand_Second));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}
	private OneWound provideStomachWound(int leLoss) {
		OneWound wound = new OneWound(Zones.Stomach);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KO,-1,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KK,-1,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.INIBase,-1,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.GS,-1,false));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1,OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1,OneNPC.Hand_Second));
		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideStomachWoundFinal(int roundsUnconscious) {
		OneWound wound = new OneWound(Zones.Stomach);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateUnconscious(roundsUnconscious));
		states.add(new StateBloodLoss(-1));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}
	private OneWound provideMainLegWound(int leLoss) {
		OneWound wound = new OneWound(Zones.MainLeg);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.GE,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.INIBase,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.GS,-1,false));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Second));
		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideMainLegWoundFinal() {
		OneWound wound = new OneWound(Zones.MainLeg);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateOnGround());
		states.add(new StateNoCloseCombat(Integer.MAX_VALUE));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}
	private OneWound provideSecondLegWound(int leLoss) {
		OneWound wound = new OneWound(Zones.SecondLeg);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.GE,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.INIBase,-2,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.GS,-1,false));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerATPA(-2, -2,OneNPC.Hand_Second));
		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideSecondLegWoundFinal() {
		OneWound wound = new OneWound(Zones.SecondLeg);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateOnGround());
		states.add(new StateNoCloseCombat(Integer.MAX_VALUE));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}

	private OneWound provideBackWound(int leLoss){
		OneWound wound = new OneWound(Zones.Back);
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KO,-1,false));
		wound.addStateChangerWound(new StateChangerAttribute(OneAttribute.KK,-1,false));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1 ,OneNPC.Hand_Main));
		wound.addStateChangerWound(new StateChangerATPA(-1, -1 ,OneNPC.Hand_Second));

		wound.addStateChangerWound(new StateChangerEnergy(OneEnergy.LE,-leLoss));
		wound.setFinalWound(false);
		return wound;
	}

	private OneWound provideBackWoundFinal(int roundsUnconscious) {
		OneWound wound = new OneWound(Zones.Back);
		List<OneState> states = new ArrayList<OneState>();
		states.add(new StateUnconscious(roundsUnconscious));
		states.add(new StateBloodLoss(-1));
		wound.addStateChangerWound(new StateChangerState(states));
		wound.setFinalWound(true);
		return wound;
	}
}
